# 一、类的定义和使用语法
"""
类的定义:
class 类名:
    属性(成员变量)
    行为(成员方法)

创建对象:
对象名 = 类名()

使用对象相关属性
对象.相关属性
"""


# 1. 设计一个类(这个类类比设计一张登记表)
class Student:
    name = None # 记录学生姓名
    gender = None # 记录学生性别
    nationality = None # 记录学生国籍
    native_place = None # 记录学生籍贯
    age = None     # 记录学生年龄

# 2. 创建对象(类比打印一张登记表)
stu_1 = Student()
# 3. 对象属性进行赋值(类比填写表单)
stu_1.name = "林俊杰"
stu_1.gender = "男"
stu_1.nationality = "中国"
stu_1.native_place = "福建省"
stu_1.age = 18

# 4. 打印
print(f"姓名:{stu_1.name}")
print(f"性别:{stu_1.gender}")
print(f"国家:{stu_1.nationality}")
print(f"籍贯:{stu_1.native_place}")
print(f"年龄:{stu_1.age}")

# 二、成员方法的定义和使用
"""
class 类名:
    def 方法名(self, 形参1, 形参2, ...):
        方法的具体实现
        
说明:
self关键字是成员方法定义的时候必须填写的。
1. 它用来表示类对象自身的意思
2. 当对象覅用方法的时候，self会被自动传入
3. 在方法内部, 想要访问成员变量, 必须使用self。如self.变量名
4. 有点C++中隐藏this指针的味道~
"""
class Stu:
    name = None # 学生姓名
    def sayHi(self): # 在方法内部, 想要访问成员变量, 必须使用self。如self.变量名
        print(f"my name is {self.name}")
    def introduce(self, msg):
        print(f"my name is {self.name}, I say that {msg}")

s1 = Stu()
s1.name = "张三"
s1.sayHi() # 通过对象调用成员方法

s2 = Stu()
s2.name = "李四"
s2.introduce('hello python')

# 三、构造方法
"""
python类可以使用:__init__()方法来达到对成员变量进行初始化操作，这个方法也称为构造方法
注意: 在创建类对象的时候，__init__()方法会被程序自动调用

class 类名:
    成员变量1
    成员变量2
    ...
    def __init__(self, 形参1, 形参2, ...):
       self.成员变量1 = 形参1
       self.成员变量2 = 形参2
       ...
说明: 成员变量的定义是可以被省略的 
"""
class Stud:
    # 成员变量可以省略
    name = None
    age = None
    tele = None

    def __init__(self, name, age, tele):
        self.name = name
        self.age = age
        self.tele = tele
        print("调用了构造方法")

s3 = Stud("张三", 19, "123456")
print(f"姓名:{s3.name}")
print(f"年龄:{s3.age}")
print(f"电话:{s3.tele}")

# 练习案例: 学生信息录入
"""
开学了有一批学生信息需要录入系统，请设计一个类，记录学生的:
姓名、年龄、地址，这3类信息
请实现:
1. 通过for循环，配合input输入语句，并使用构造方法，完成学生信息的键盘录入
2. 输入完成后，使用print语句，完成信息的输出

输入样例:
当前录入第1位学生信息，总共需录入10位学生信息请输入学生姓名:周杰轮
请输入学生年龄:31请输入学生地址:北京
学生1信息录入完成，信息为:【学生姓名:周杰轮，年龄:31，地址:北京】

当前录入第2位学生信息，总共需录入10位学生信息请输入学生姓名:
"""
"""
class student:
    name = None
    age = None
    address = None

    def __init__(self):
        for i in range(1, 11):
            print(f"当前录入第{i}位学生信息，总共需要录入10位学生的信息")
            name = input("请输入学生姓名: ")
            self.name = name
            age = input("请输入学生年龄: ")
            self.age = age
            address = input("请输入学生地址: ")
            self.address = address
            print(f"学生{i}的信息录入完成, 信息为:【学生姓名:{name}, 年龄{age}, 地址:{address}】")

s1 = student()
"""

# 四、其它内置方法（魔术方法）
"""
__init__方法是Python类内置的方法之一。这些内置方法各有各的特殊功能，其中内置方法又称魔术方法
1. __init__  --> 构造方法
2. __str__   --> 字符串方法
3. __lt__    --> 小于大于符号比较
4. __le__    --> 小于等于、大于等于符号比较
5. __eq__    --> 相等比较符号
魔术方法的命名规范: __魔术方法名__
"""

# 1. 字符串方法
"""
当打印字符串对象的时候，打印的结果往往都是内存地址，而内存地址没有多大作用，
因此，__str__方法可以控制类转换为字符串的行为
"""
class Test1:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name} {self.age}"

t1 = Test1("王五", 11)
print(t1)

class Test2:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __lt__(self, other): # self是左操作数对象，others是右操作数对象
        return self.age < other.age

t1 = Test2("王五", 11)
t2 = Test2("李四", 12)
if (t1 > t2):
    print("王五的年纪大")
else:
    print("李四的年纪大")


class Test3:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __le__(self, other): # self是左操作数对象，others是右操作数对象
        return self.age < other.age

t1 = Test3("王五", 11)
t2 = Test3("李四", 12)
if (t1 >= t2):
    print("王五的年纪大")
else:
    print("李四的年纪大")


class Test4:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other): # self是左操作数对象，others是右操作数对象
        return self.age < other.age

t1 = Test4("王五", 12)
t2 = Test4("李四", 12)
if (t1 == t2):
    print("王五和李四的年龄一样大")
else:
    print("王五和李四的年龄不一样大")

# 五、面向对象的三大特性之一 --- 封装
"""
什么是封装？

将现实事物在类中描述为属性和方法即为封装
然而现实事物的部分属性和方法是不公开对使用者开放的，因此在类中就要将其定义为私有的

私有成员变量和私有成员方法

1. 定义
私有成员变量: 变量名以两个下划线开头
私有成员方法: 方法名以两个下划线开头

2. 特点
类外无法使用这些私有成员变量和私有成员方法, 但是类内的其它成员是可以访问这些私有成员的
"""

class Phone:
    __current_voltage = 1 # 手机的运行电压

    def __keep_single_core(self):
        print("让CPU以单核模式运行")

    def call_by_5G(self):
        if self.__current_voltage >= 1:
            print("5G通话已开启")
        else:
            self.__keep_single_core()
            print("电量不足，无法使用5G通话，并已设置为单核运行进行省电")
p1 = Phone()
p1.call_by_5G()
# 类外无法使用这些私有成员变量和私有成员方法
# Phone.__keep_single_core()
# print(p1.__current_voltage)

# 练习案例: 设计带有私有成员的手机
"""
设计一个手机类，内部包含:
1. 私有成员变量: is_5g_enable，类型bool，True表示开启5g，False表示关闭5g
2. 私有成员方法: check_5g()，会判断私有成员 is 5g_enable的值
        若为True，打印输出:5g开启
        若为False，打印输出:5g关闭，使用4g网络
3. 公开成员方法:call by_5g()，调用它会执行
        调用私有成员方法:_check_5g()，判断5g网络状态
        打印输出:正在通话中

运行结果:
5g关闭，使用4g网络
正在通话中

通过完成这个类的设计和使用，体会封装中私有成员的作用
对用户公开的，call by_5g()方法
对用户隐藏的，_is_5g_enable私有成员变量和_check_5g()私有成员
"""
class phone:
    __is_5g_enable = False

    def __check_5g(self):
        if self.__is_5g_enable == True:
            print("5g开启")
        else:
            print("5g关闭，使用4g网络")

    def call_by_5g(self):
        self.__check_5g()
        print("正在通话中")

